#include <iostream>
#include <vector>
#include <cassert>
#include <algorithm>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>

int g (int a, int b)
{
	return a + b;
}

struct F
{
    int operator()(int a, int b) { return a - b; }
    bool operator()(long a, long b) { return a == b; }
};

/**
 * Sum up. 
 */
struct F2
{
    int s;
    typedef void result_type;
    void operator()( int x ) { s += x; }
};

/**
 * Incrementor
 */
struct F3
{
    typedef void result_type;
    void operator()( int & x ) { x+= 2; }
};


// Using bind with functions and function pointers.
void test_01()
{


    // The first parameter is variables; the second paramter is binded to be 5. 
	int a1 = boost::bind (g, _1, 5) (4); 
	assert (a1 == 9);
	
    // The first paramter is binded to be seven ; 
	int a2 = boost::bind (g, 7, _1) (1); 
	assert (a2 == 8);

	// The first and second are binded to be the same as the first paramter; g (a, a)
	int a3 = boost::bind (g, _1, _1) (2, 3); 
	assert (a3 == 4);

    // The first and second are binded to be the same as the second paramter; g (b, b)
	int a4 = boost::bind (g, _2, _2) (2, 3); 
	assert (a4 == 6);

    // The first and second are swapped. // g (b, a); 
	int a5 = boost::bind (g, _2, _1) (2, 3); 
	assert (a5 == 5);

	// boost::ref  use reference instead of the internal value copy. 
	// boost::cref use const_reference instead of the interal value copy . 
	int v = 1; 
	int a6 = boost::bind (g, boost::ref(v), _1 ) (7);
	assert (a6 == 8);
}

// Use bind with function objects. 
void test_02()
{

	F f;
	int x = 104;
	// Alternative. boost::bind(boost::type<int>(), f, _1, _1)(x);
	boost::bind<int>(f, _1, _1)(x);		// f(x, x), i.e. zero

	// Sum up. 
	F2 f2 = { 0 };
	int a[] = { 1, 2, 3 };
	std::for_each( a, a+3, bind( boost::ref(f2), _1 ) );
	assert( f2.s == 6 );

	// Incrementor
	F3 f3 ;
	std::for_each(a, a + 3, bind (boost::ref(f3), _1));
	assert (a[2] == 5 && a[1]== 4 && a[0] == 3);

	// Sort 
}

// use bind with member function . 
struct X
{
    int f(int)
	{
		return 0; 
	}
};


void test_03()
{
	X x;
	int v ;
	v = boost::bind(&X::f, x , _1) (5);  // Internal copy of x; 
	assert (v == 0);
	v = boost::bind(&X::f, boost::ref(x), _1) (5); // Reference of x ;
	assert (v == 0);
	v = boost::bind(&X::f, &x , _1) (5); // Pointer of x ; (&x)->f(); 
	assert (v == 0);
}

/**
 * Sort example. 
 */
int diff (int g)
{
	return std::abs(g - 3);
}
int diff2 (int a, int b)
{
	return std::abs(a-b);
}
struct Diff
{
	int diff (int g)
	{
		return std::abs(g - 4);
	}

};
void test_04()
{
	std::vector<int> int_v; 
	for (unsigned i = 0; i < 10; ++i) int_v.push_back(i);
	// Sort the number with respect to the distance from 3. 
	std::sort(int_v.begin(), int_v.end(), boost::bind (diff, _1) < boost::bind(diff, _2));
	std::for_each (int_v.begin(), int_v.end(), std::cout<< boost::lambda::_1 <<' ');
	std::cout<<std::endl;

	// Sort the number with respect to the distance from 4. 
	Diff obj ;
	std::sort(int_v.begin(), int_v.end(), boost::bind (&Diff::diff, boost::ref(obj), _1) < boost::bind(&Diff::diff, boost::ref(obj), _2));
	std::for_each (int_v.begin(), int_v.end(), std::cout<< boost::lambda::_1 <<' ');
	std::cout<<std::endl;

        // Sort the number with respect to the distance from 4. 
        std::sort(int_v.begin(), int_v.end(), boost::bind (diff2, _1, 4) < boost::bind(diff2, _2 , 4 ));
        std::for_each (int_v.begin(), int_v.end(), std::cout<< boost::lambda::_1 <<' ');
        std::cout<<std::endl;


}

int main()
{
	test_01(); 
	test_02(); 
	test_03(); 
	test_04();

	return 0;
}

